package com.wa.my.config;

import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.allchips.common.RestAPICode;
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.protocol.Packet;
import com.corundumstudio.socketio.protocol.PacketType;
import com.wa.allchips.util.CommUtils;
import com.wa.base.GenericResp;
import com.wa.constants.NameSpaceBeanNameConstant;
import com.wa.constants.WebSocketEventConstant;
import com.wa.enums.NameSpaceEnum;
import com.wa.my.bean.ChatObject;
import com.wa.my.handler.SocketConnectHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;
import java.util.List;
import java.util.UUID;

/**
 * @author gary
 * @date 2021年12月23日 11:04
 */
@Configuration
public class SockerServer {

    private Logger logger = LoggerFactory.getLogger(SockerServer.class);

    private SocketIOServer socketIOServer;

    @Bean(initMethod = "start", destroyMethod = "stop")
    public SocketIOServer socketIOServer() {
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setHostname("localhost");
        config.setPort(9093);

        config.setAuthorizationListener(authorizationListener());

        SocketIOServer server = new SocketIOServer(config);

        SocketIONamespace chat1namespace = server.addNamespace("/chat1");
        chat1namespace.addConnectListener(socketConnectHandler());
        chat1namespace.addEventListener("message", ChatObject.class, (client, data, ackRequest) -> {
            // broadcast messages to all clients
            chat1namespace.getBroadcastOperations().sendEvent("message", data);
        });

        SocketIONamespace chat2namespace = server.addNamespace("/chat2");
        chat2namespace.addEventListener("message", ChatObject.class, (client, data, ackRequest) -> {
            // broadcast messages to all clients
            chat2namespace.getBroadcastOperations().sendEvent("message", data);
        });
        socketIOServer = server;
        return server;
    }

    @Bean
    public SocketConnectHandler socketConnectHandler() {
        return new SocketConnectHandler();
    }


    /**
     * 权限校验
     * 返回false的话，前端爆红
     *
     * @return
     */
    private AuthorizationListener authorizationListener() {
        return data -> {
            //从地址中获取参数  例子：wss://testxqq.allchips.com/ws/?a=3344&b=6090&EIO=3&transport=websocket
            String from = data.getSingleUrlParam("b");
            Integer userType = Convert.toInt(from);
            // 后端用户校验
//            if (Objects.equals(userType, UserTypeConstant.BACKEND_USER)) {
//                return backendLoginAuthor.authorize(WebSocketUtil.getHttpRequestToken(data.getHttpHeaders().get(WebSocketUtil.COOKIE_HEADER_NAME)));
//            }
            // 前端用户校验
//            if (Objects.equals(userType, UserTypeConstant.FRONT_USER)) {
//                return frontLoginAuthor.authorize("");
//            }
            // 游客校验
//            if (Objects.equals(userType, UserTypeConstant.VISITOR)) {
//                return visitorLoginAuthor.authorize("");
//            }
            return true;
        };
    }

    /**
     * 是否在线
     *
     * @param service
     * @param uuid
     * @return
     */
    public GenericResp<Boolean> online(int service, String uuid) {
        GenericResp<Boolean> returnVo = new GenericResp<>();
        try {
            SocketIONamespace namespace = null;
            if (CommUtils.isNotNull(NameSpaceEnum.get(service))) {
                namespace = SpringUtil.getBean(NameSpaceEnum.get(service).getCodeCn() + NameSpaceBeanNameConstant.SUFFIX);
            }
            final SocketIONamespace finalNamespace = namespace;
            if (null != finalNamespace) {
                returnVo.setData(getSocketClient(finalNamespace, uuid) == null ? Boolean.FALSE : Boolean.TRUE);
            } else {
                returnVo.setMessage("命名空间不存在");
                returnVo.setCode(RestAPICode.DATABASE_ERROR.getCode());
            }
        } catch (Exception e) {
            logger.error("推送异常", e);
            returnVo.setCode(RestAPICode.DATABASE_ERROR.getCode());
        }
        return returnVo;
    }

    /**
     * @param socketIONamespace
     * @param uuid
     * @return
     */
    private SocketIOClient getSocketClient(SocketIONamespace socketIONamespace, String uuid) {
        UUID uid = null;
        try {
            uid = UUID.fromString(uuid);
        } catch (Exception e) {
            logger.debug("uid输入不正确");
        }
        SocketIOClient client;
        try {
            client = socketIONamespace.getClient(uid);
        } catch (Exception e) {
            return null;
        }
        // 命名空间没有的情况下, 从根空间获取, 会导致前端无法监听事件, 监听事件是跟命名空间绑定的
        if (null == client || !client.isChannelOpen()) {
            client = socketIOServer.getClient(UUID.fromString(uuid));
        }
        return client;
    }

    /**
     *
     * @param id
     * @param message
     * @param event
     * @param client
     * @return
     */
    private GenericResp<Boolean> doPush(String id, Object message, String event, SocketIOClient client) {
        if (null == client || !client.isChannelOpen()) {
            logger.info("用户不在线{id/uuid:{}}", id);
            return offlineReturnVo(id);
        }
        try {
            if (CommUtils.isEmpty(event)) {
                event = WebSocketEventConstant.MESSAGE_EVENT;
            }
            if (message instanceof String) {
                String copyMessage = (String) message;
                boolean decoded = true;
                try {
                    message = JSONObject.parseObject(copyMessage);
                } catch (Exception e) {
                    decoded = false;
                }
                if (!decoded) {
                    try {
                        decoded = true;
                        message = JSONArray.parseArray(copyMessage);
                    } catch (Exception e) {
                        decoded = false;
                    }
                }
                if (!decoded) {
                    logger.info("不是json串, 作为普通串发布");
                }
            }
            client.send(packet(event, message));
        } catch (Exception e) {
            logger.error("推送异常", e);
            return errorReturnVo(id);
        }
        return successReturnVo(id);
    }

    public Packet packet(String event, Object data) {
        Packet packet = new Packet(PacketType.MESSAGE);
        packet.setSubType(PacketType.EVENT);
        packet.setName(event);
        if (data instanceof List) {
            packet.setData(data);
        } else {
            packet.setData(Collections.singletonList(data));
        }
        return packet;
    }

    static GenericResp<Boolean> successReturnVo(Object param) {
        GenericResp<Boolean> returnVo = new GenericResp<>();
        returnVo.setData(Boolean.TRUE);
        returnVo.setMessage(String.format("推送成功：%s", JSONUtil.toJsonStr(param)));
        return returnVo;
    }

    static GenericResp<Boolean> errorReturnVo(Object param) {
        GenericResp<Boolean> returnVo = new GenericResp<>();
        returnVo.setCode(RestAPICode.DATABASE_ERROR.getCode());
        returnVo.setData(Boolean.FALSE);
        returnVo.setMessage(String.format("推送异常: %s", JSONUtil.toJsonStr(param)));
        return returnVo;
    }

    static GenericResp<Boolean> offlineReturnVo(Object param) {
        GenericResp<Boolean> returnVo = new GenericResp<>();
        returnVo.setCode(RestAPICode.DATABASE_ERROR.getCode());
        returnVo.setData(Boolean.FALSE);
        returnVo.setMessage(String.format("未上线: %s", JSONUtil.toJsonStr(param)));
        return returnVo;
    }
}
